package com.ftqh.security.oauth2.config;

import com.ftqh.security.oauth2.provider.approval.Oauth2UserApprovalHandler;
import com.ftqh.security.oauth2.provider.client.OAuth2ClientDetailsService;
import com.ftqh.security.oauth2.provider.code.RedisAuthorizationCodeServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.*;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

/**
 * \* Created with IntelliJ IDEA.
 * \* User: Administrator
 * \* Date: 2016/12/29
 * \* Time: 15:10
 * \* To change this template use File | Settings | File Templates.
 * \* Description:
 * 为什么不用自动配置。因为/oauth/check_token默认是denyAll.
 * 必须手动设置oauthServer.checkTokenAccess("isAuthenticated()");才访问能验证Access Token。
 * \
 */
@Configuration
@EnableAuthorizationServer
public class Oauth2AuthoriationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    //see AuthorizationServerConfig->authenticationManagerBean
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisConnectionFactory connectionFactory;

    /**
     * oauth_client_details will store in mysql
     * @return
     */
    @Bean
    public ClientDetailsService clientDetailsService(){
        return new OAuth2ClientDetailsService();
    }
    /**
     * token(accessToken, RefreshToken) will store in redis
     */
    @Bean
    public TokenStore tokenStore(){
        return new RedisTokenStore(connectionFactory);
    }

    /**
     *  reference AuthorizationServerEndpointsConfigurer -> private approvalStore()
     * @return
     * @throws Exception
     */
    @Bean
    public ApprovalStore approvalStore() throws Exception {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore());
        return store;
    }
    @Bean
    @Lazy
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    public Oauth2UserApprovalHandler userApprovalHandler() throws Exception {
        Oauth2UserApprovalHandler handler = new Oauth2UserApprovalHandler();
        handler.setApprovalStore(approvalStore());
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService()));
        handler.setClientDetailsService(clientDetailsService());
        //add a useApprovalStore property
        handler.setUseApprovalStore(true);
        return handler;
    }
    /**
     * authorization_code will store in redis
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        return new RedisAuthorizationCodeServices();
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        //defalut realm is "oauth2/client"
        //oauthServer.realm("oauth2/client");
        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())
                .approvalStore(approvalStore())
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices());
    }

}
